package edu.scau.pos.refund.service;

import cn.hutool.core.util.IdUtil;
import com.alipay.api.AlipayApiException;
import com.fasterxml.jackson.core.JsonProcessingException;
import edu.scau.core.Enum.OrderStatus;
import edu.scau.core.Enum.RefundStatus;
import edu.scau.core.Enum.RefundType;
import edu.scau.core.Enum.SaleLineItemStatus;
import edu.scau.core.exection.BusinessProcessException;
import edu.scau.payment.service.PaymentService;
import edu.scau.pos.domain.Orders;
import edu.scau.pos.domain.SaleLineItem;
import edu.scau.pos.refund.domain.RefundRecord;
import edu.scau.pos.refund.domain.RefundRecordItem;
import edu.scau.pos.service.OrdersService;
import edu.scau.pos.service.ProductService;
import edu.scau.pos.service.SaleLineItemService;
import edu.scau.pos.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AfterSaleServiceImpl implements AfterSaleService {
    @Autowired
    private OrdersService ordersService;
    @Autowired
    private ProductService productService;
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private SaleLineItemService saleLineItemService;
    @Autowired
    private RefundRecordService refundRecordService;
    @Override
    public List<OrdersVo> listAll() {
        return List.of();
    }

    /**
     * 顶层退款方法
     * @param refundOrderVo
     * @return
     */
    @Override
    public Map refundService(RefundOrderVo refundOrderVo) throws AlipayApiException, JsonProcessingException {
        RefundRecord refundRecord = this.changeRefundVoIntoRecord(refundOrderVo);
        refundRecord.setRefundDate(new Date());
        this.checkRefundRule(refundOrderVo);
        Map res = new HashMap();
        if(refundOrderVo.getType()==RefundType.cancel.getSignal()){
            res=this.cancelBook(refundOrderVo.getOrderId(),refundRecord.getRefundId());
        }
        if(refundOrderVo.getType()==RefundType.refund.getSignal()){
            res=this.refund(refundOrderVo,refundRecord.getRefundId());
        }
        int status= (Integer) res.get("status");
        refundRecord.setRefundAmount((Double) res.get("refundAmount"));
        try {
            this.saveToDB(refundRecord);
        } catch (Exception e) {
            log.error("保存 refundRecord 异常", e);
        }
        try{
            this.saveToDB(refundRecord);
        }
        catch (Exception e){
            log.info(e.getMessage());
        }
        return res;
    }


    /**
     * 重新退款
     * @param refundId
     * @return
     */
    @Override
    public Map reRefund(String refundId) throws AlipayApiException, JsonProcessingException {
        RefundRecord refundRecord=refundRecordService.getRefundRecord(refundId);
        if(refundRecord==null){
            throw new BusinessProcessException("找不到退款记录！");
        }
        if(refundRecord.getStatus()!= RefundStatus.refund.getSignal()){
            throw new BusinessProcessException("退款已完成，不可重复申请！");
        }
        RefundOrderVo refundOrderVo=this.changeIntoRefundOrderVo(refundRecord);

        return this.refundService(refundOrderVo);
    }

    private RefundOrderVo changeIntoRefundOrderVo(RefundRecord refundRecord) {
        RefundOrderVo refundOrderVo=new RefundOrderVo();
        refundOrderVo.setOrderId(refundRecord.getOrderId());
        refundOrderVo.setRefundItemList(this.initRefundItemList(refundRecord.getRefundRecordItemList()));
        refundOrderVo.setApplyDate(new Date());
        refundOrderVo.setType(refundRecord.getType());
        return refundOrderVo;
    }

    private List<SaleLineItemVo> initRefundItemList(List<RefundRecordItem> refundRecordItemList) {
        if(refundRecordItemList==null || refundRecordItemList.size()==0){
            return new ArrayList<>();
        }
        List<SaleLineItemVo> saleLineItemVoList=new ArrayList<>();
        for (RefundRecordItem refundRecordItem : refundRecordItemList) {
            SaleLineItemVo saleLineItemVo=new SaleLineItemVo();
            saleLineItemVo.setProductSn(refundRecordItem.getProductSn());
            saleLineItemVo.setQuantity(refundRecordItem.getQuantity());
            saleLineItemVoList.add(saleLineItemVo);
        }
        return saleLineItemVoList;
    }

    @Async
    public void saveToDB(RefundRecord refundRecord) {
            OrdersVo ordersVo=ordersService.findOrdersByOrderId(refundRecord.getOrderId());
            List<SaleItemVo> list=ordersVo.getSaleLineItems();
            List<RefundRecordItem> refundRecordItemList=this.changeSaleItemIntoRefundRecordItem(list,refundRecord.getRefundId());
            refundRecord.setRefundRecordItemList(refundRecordItemList);
            refundRecordService.addOrUpdateRefundRecord(refundRecord);
    }

    private List<RefundRecordItem> changeSaleItemIntoRefundRecordItem(List<SaleItemVo> list,String refundId) {
        if(list==null || list.isEmpty()){
            return new ArrayList<>();
        }
        List<RefundRecordItem> refundRecordItemList=new ArrayList<>();
        for (SaleItemVo saleItemVo : list) {
            RefundRecordItem refundRecordItem=new RefundRecordItem();
            refundRecordItem.setProductSn(saleItemVo.getProductSn());
            refundRecordItem.setQuantity(saleItemVo.getQuantity());
            refundRecordItem.setProductName(saleItemVo.getProductName());
            refundRecordItem.setRefundId(refundId);
            refundRecordItemList.add(refundRecordItem);
        }
        return refundRecordItemList;
    }


    private RefundRecord changeRefundVoIntoRecord(RefundOrderVo refundOrderVo) {
        RefundRecord refundRecord=new RefundRecord(refundOrderVo);
        refundRecord.setRefundId("refund_"+ IdUtil.getSnowflakeNextId());
        List<RefundRecordItem> refundRecordItemList=new ArrayList<>();
        if(!refundOrderVo.getRefundItemList().isEmpty()){
            for (int i = 0; i < refundOrderVo.getRefundItemList().size(); i++) {
                RefundRecordItem item=new RefundRecordItem();
                item.setProductSn(refundOrderVo.getRefundItemList().get(i).getProductSn());
                item.setQuantity(refundOrderVo.getRefundItemList().get(i).getQuantity());
                item.setProductName(productService.findProductBySn(refundOrderVo.getRefundItemList().get(i).getProductSn()).getProductName());
                refundRecordItemList.add(item);
            }
            refundRecord.setRefundRecordItemList(refundRecordItemList);
        }
        return refundRecord;
    }


    /**
     * 部分商品退货（成功/挂单订单）
     * @param refundOrderVo
     * @return
     */
    @Override
    @Transactional
    public Map refund(RefundOrderVo refundOrderVo,String refundId) throws AlipayApiException, JsonProcessingException,BusinessProcessException {
        Orders originOrder=ordersService.findByOrderId(refundOrderVo.getOrderId());
        String outTradeId=this.getOutTradeId(originOrder);
        String desc=refundOrderVo.getDesc();
        //计算退款金额
        if(refundOrderVo.getRefundItemList() == null || refundOrderVo.getRefundItemList().size() == 0){
            throw new BusinessProcessException("未选择退货商品！");
        }
        double refundAmount=this.calculateRefundAmount(refundOrderVo.getRefundItemList(),originOrder.getSaleLineItems());
        if(refundAmount>originOrder.getAmount()){
            throw  new BusinessProcessException("退款金额超额，请检查！");
        }
        //sdk
        HashMap<String,Object> data=new HashMap<>();
        try {
            Map refundMap=paymentService.refund(outTradeId,refundAmount,desc,refundId);
            data.put("status",RefundStatus.cancel.getSignal());
            data.put("msg",refundMap.get("msg"));

            data.put("refundAmount",Double.valueOf((String) refundMap.get("send_back_fee")));
            data.put("refundTime",refundMap.get("gmt_refund_pay"));
            //设置状态
            //取消挂单item\order状态
            this.Restock(refundOrderVo,originOrder);
            this.updateSaleItemStatus(refundOrderVo,originOrder);
            this.updateOrderAmountAndQuantity(originOrder,(String) refundMap.get("send_back_fee"),refundOrderVo);

            //更新订单金额
            //入库
        }catch (BusinessProcessException e){
            data.put("status",RefundStatus.refund.getSignal());
            data.put("msg",e.getMessage());
            double amount=0;
            data.put("refundAmount", amount);
        }
        return data;
    }

    /**
     * 取消预订订单
     * @param orderId
     * @return
     */
    @Override
    public Map cancelBook(String orderId,String refundId) throws AlipayApiException, JsonProcessingException {
        Orders blockOrders=ordersService.findByOrderId(orderId);
        if(blockOrders.getStatus()!= OrderStatus.Backorder.getSignal()){
            throw new BusinessProcessException("不能取消非预订订单");
        }
        String ourTradeId=this.getOutTradeId(blockOrders);
        String desc="预订订单取消";
        double refundFee=blockOrders.getAmount();
        HashMap<String,Object> data=new HashMap<>();

       try{
           Map resultMap=paymentService.refund(ourTradeId,refundFee,desc,refundId);
           data.put("status",RefundStatus.cancel.getSignal());
           data.put("msg",resultMap.get("msg"));
           data.put("refundAmount",Double.valueOf((String)resultMap.get("send_back_fee")));
           data.put("refundTime",resultMap.get("gmt_refund_pay"));
           //更新order状态
           this.updateBlockOrderStatus(blockOrders);
       }catch (BusinessProcessException e){
           data.put("status",RefundStatus.refund.getSignal());
           data.put("msg",e.getMessage());
           double amount=0;
           data.put("refundAmount", amount);
       }
        return  data;
    }

    private void checkIfRefund(Orders orders) {
        if(orders.getStatus()== OrderStatus.Refunded.getSignal()){
            throw new BusinessProcessException("订单已退款，不能重复退款");
        }
    }

    /**
     * 检查逻辑问题
     * @param refundOrderVo
     */
    private void checkRefundRule(RefundOrderVo refundOrderVo) {
        Orders orders=ordersService.findByOrderId(refundOrderVo.getOrderId());
        this.checkIfRefund(orders);
        //取消预定
        if(refundOrderVo.getType()== RefundType.cancel.getSignal()){
            if(orders.getStatus()!= OrderStatus.Backorder.getSignal()){
                throw new BusinessProcessException("只能取消预定的订单！");
            }
        }
        //挂单订单也可取消部分商品
//        if(refundOrderVo.getType()== RefundType.refund.getSignal()){
//            if(orders.getStatus()!= OrderStatus.Paid.getSignal()){
//                throw new BusinessProcessException("操作类型和订单类型不符");
//            }
//        }
    }

    @Transactional
    @Async
    public void updateBlockOrderStatus(Orders blockOrders) {
        List<SaleLineItem> saleLineItems=blockOrders.getSaleLineItems();
        for (SaleLineItem saleLineItem : saleLineItems) {
            saleLineItem.setStatus(SaleLineItemStatus.refunded.getSignal());
        }
        blockOrders.setStatus(OrderStatus.Refunded.getSignal());
        int r1= saleLineItemService.updateBatch(saleLineItems);
        if(r1!=saleLineItems.size()){
            log.info("取消预订条目状态更新失败");
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        int r2=ordersService.updateOrders(blockOrders);
        if(r2<=0){
            log.info("取消预订更新订单状态失败");
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

    /**
     * 商品重新入库
     * @param refundOrder
     */
    @Async
    public void Restock(RefundOrderVo refundOrder,Orders originOrder) {
        int status=originOrder.getStatus();
        List<SaleLineItemVo> saleLineItemVoList=refundOrder.getRefundItemList();
        //
        if(status==OrderStatus.Paid.getSignal()){
            this.updateSkuBySn(saleLineItemVoList);
        }
        //OrderStatus.Backorder.getSignal() -->直接更新saleItem状态

    }

    @Transactional
    public boolean updateSkuBySn(List<SaleLineItemVo> saleLineItemVoList)  {
        int res = productService.batchAddStock(saleLineItemVoList);
        if( res == saleLineItemVoList.size()){
            return true;
        }else {
            log.info("批量出库失败，中断重新入库事务，抛出异常");
            throw new RuntimeException("入库失败");
        }
    }

    /**
     * 更新退款后订单金额
     * @param sendBackFee
     */
    @Async
    public void updateOrderAmountAndQuantity(Orders orders, String sendBackFee,RefundOrderVo refundOrderVo) {
        int refundQuantity=0;
        for (SaleLineItemVo saleLineItemVo : refundOrderVo.getRefundItemList()) {
            refundQuantity+=saleLineItemVo.getQuantity();
        }
        double sendBackFee1=Double.parseDouble(sendBackFee);
        double remainAmount=orders.getAmount()- sendBackFee1;
        orders.setAmount(remainAmount);
//        int quantity=0;
        //取出重新入库的细目
        //会回滚，未更新
//        List<SaleLineItem> saleLineItems=ordersService.findByOrderId(orders.getOrderId()).getSaleLineItems();
//        for (SaleLineItem saleLineItem : saleLineItems) {
//            //退货的商品直接除去数量
//                quantity+=saleLineItem.getQuantity();
//        }
        int releaseQuantity=orders.getQuantity()-refundQuantity;
        orders.setQuantity((releaseQuantity));
        if(ordersService.updateOrders(orders) <= 0){
            log.info("订单更新失败");
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            throw new BusinessProcessException("退货失败");
        }
    }

    /**
=======

    }

    /**
     * 检查saleItem的正常加购、完成出库状态有没有设置
     * @param refundOrderVo
     */
    @Transactional
    @Async
    public void updateSaleItemStatus(RefundOrderVo refundOrderVo,Orders orders) {
        List<SaleLineItem> saleLineItems=orders.getSaleLineItems();
        List<SaleLineItem> copySaleLineItems=saleLineItems.subList(0,saleLineItems.size());
        Map<String,SaleLineItemVo> refundMap=refundOrderVo.getRefundItemList().stream().collect(Collectors.toMap(
                SaleLineItemVo::getProductSn,
                item->item,
                (existing, replacement) -> existing
        ));
        Iterator<SaleLineItem> iterator = copySaleLineItems.iterator();
        while (iterator.hasNext()) {
            SaleLineItem saleLineItem = iterator.next();
            if (refundMap.containsKey(saleLineItem.getProductSn())) {
                ProductVo productVo = productService.findProductBySn(saleLineItem.getProductSn());
                double refundFee = productVo.getPrice() * refundMap.get(saleLineItem.getProductSn()).getQuantity();
                int qt = saleLineItem.getQuantity() - refundMap.get(saleLineItem.getProductSn()).getQuantity();

                if (qt == 0) {
                    // 全部退货
                    saleLineItem.setStatus(SaleLineItemStatus.refunded.getSignal());
                }

                saleLineItem.setQuantity(qt);
                saleLineItem.setAmount(saleLineItem.getAmount() - refundFee);
            } else {
                iterator.remove();
            }
        }
        //全部退货-->可能存在全选的状态
        if(copySaleLineItems.size() == saleLineItems.size()){
            orders.setStatus(OrderStatus.Refunded.getSignal());
        }
        //把saleLineItemList挂进去返回入库 ×
        //批量更新修改后的saleLineItem
       if( saleLineItemService.updateBatch(copySaleLineItems) < copySaleLineItems.size()){
           log.info("'部分退货'细目更新退货状态失败！");
           TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
       }
        //order入库-->转到另一个方法

    }


    /**
     * 查询退货状态
     * @param refundId
     * @return
     */
    @Override
    public Map queryRefundStatus(String refundId) throws AlipayApiException, JsonProcessingException {
        String orderId=refundRecordService.getRefundRecord(refundId).getOrderId();
        Orders queryOrders=ordersService.findByOrderId(orderId);
        if(queryOrders==null){
            throw new BusinessProcessException("订单不存在");
        }
        String outTradeId=this.getOutTradeIdForRefund(queryOrders);
        return paymentService.queryRefundStatus(refundId,outTradeId);
    }


    /**
     * 计算退款金额
     * @param refundItemList
     * @return
     */
    private double calculateRefundAmount(List<SaleLineItemVo> refundItemList,List<SaleLineItem> originSaleLineItems) {
        double refundAmount=0;
        Map<String,SaleLineItem> originSaleLineItemsMap=originSaleLineItems.stream().collect(Collectors.toMap(
                SaleLineItem::getProductSn,
                item->item,
                (existing, replacement) -> existing
        ));
        for (SaleLineItemVo saleLineItemVo : refundItemList) {
            if (! originSaleLineItemsMap.containsKey(saleLineItemVo.getProductSn())){
                throw new BusinessProcessException("存在错误退货商品");
            }
            ProductVo product=productService.findProductBySn(saleLineItemVo.getProductSn());
            if(product==null){
                throw new BusinessProcessException("商品不存在");
            }
            //是否数量不对
            if(saleLineItemVo.getQuantity()>originSaleLineItemsMap.get(saleLineItemVo.getProductSn()).getQuantity()){
                throw new BusinessProcessException("退货商品数量超出加购数量！");
            }
            refundAmount+=product.getPrice()*saleLineItemVo.getQuantity();
        }
        return refundAmount;
    }

    /**
     * 退款
     * @param refundOrder
     */
    private String getOutTradeId(Orders refundOrder) {
        String outTradeId="";
        //挂单-->successOrderId
        if(refundOrder.getStatus()==OrderStatus.Backorder.getSignal()){
            //获取saleId-->successOrder-->Id
             outTradeId = ordersService.findRelatedOrder(refundOrder.getOrderId()).getOrderId();
        }
        else {
            outTradeId = refundOrder.getOrderId();
        }
        return outTradeId;
    }

    private String getOutTradeIdForRefund(Orders orders){
        String outTradeId="";
        List<OrdersVo> ordersList=ordersService.findOrdersOfSale(orders.getOrderId());
        if(ordersList.size()==1){
            return  ordersList.get(0).getOrderId();
        }
        for(OrdersVo ordersVo:ordersList){
            if(ordersVo.getStatus()==OrderStatus.Paid.getSignal()){
                outTradeId=ordersVo.getOrderId();
            }
        }

        return outTradeId;
    }



}
